﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Kevin.SLUserControlLibrary
{
    public class TimeTextBox : TextBox
    {
        private bool inOnShift = false;
        private bool inOnCtrl = false;

        //Ctrl+V to Save OldState
        private string textTemp = "00:00";
        private int indexTemp = 0;
        private int lengthTemp = 0;
        private bool isNullable = false;

        //Ctrl+V to Check Reg
        private string regHMS = @"^([0-1]?[0-9]|2[0-4])(:[0-5]?[0-9](:[0-5]?[0-9])?)?$";
        private string regHM = @"^([0-1]?[0-9]|2[0-4])(:[0-5]?[0-9])?$";
        private string regMS = @"^[0-5]?[0-9](:[0-5]?[0-9])?$";

        public enum EnumTimeType
        {
            None,
            HourMinuteSecond,
            HourMinute,
            MinuteSecond,
        }

        //Time Banding
        public static DependencyProperty TimeTypeProperty = DependencyProperty.Register("TimeType",
           typeof(EnumTimeType), typeof(TimeTextBox), new PropertyMetadata(EnumTimeType.None, new PropertyChangedCallback(OnTimeTypePropertyChanged)));

        private static void OnTimeTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        private EnumTimeType timeType = EnumTimeType.None;
        public EnumTimeType TimeType
        {
            get
            {
                return this.timeType;
                //return (EnumTimeType)GetValue(TimeTypeProperty); 
            }
            set
            {
                this.timeType = value;
                //SetValue(TimeTypeProperty, value);
            }
        }

        //TimeType Banding
        public static DependencyProperty TimeProperty = DependencyProperty.Register("Time",
           typeof(int), typeof(TimeTextBox), new PropertyMetadata(-1, new PropertyChangedCallback(OnTimePropertyChanged)));

        private static void OnTimePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            GetStrTime((TimeTextBox)d, (int)e.NewValue);
        }

        public static DependencyProperty IsNullableProperty = DependencyProperty.Register("IsNullable",
         typeof(bool), typeof(TimeTextBox), new PropertyMetadata(false, new PropertyChangedCallback(OnTimePropertyChanged)));

        private static void OnIsNullablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // GetStrTime((TimeTextBox)d, (int)e.NewValue);
        }

        public bool IsNullable
        {
            get
            {
                return this.isNullable;
            }
            set
            {
                this.isNullable = value;
            }
        }

        private static void GetStrTime(TimeTextBox timeTextBox, int value)
        {
            if (value >= 0)
            {
                int h = value / 3600;
                int m = (value % 3600) / 60;
                int s = value % 60;
                if (timeTextBox.timeType == EnumTimeType.HourMinute)
                {
                    timeTextBox.Text = h.ToString("00") + ":" + m.ToString("00");
                }
                else if (timeTextBox.timeType == EnumTimeType.MinuteSecond)
                {
                    timeTextBox.Text = m.ToString("00") + ":" + s.ToString("00");
                }
                else
                {
                    timeTextBox.Text = h.ToString("00") + ":" + m.ToString("00") + ":" + s.ToString("00");
                }
            }
            else
            {
                timeTextBox.Text = string.Empty;
            }
        }

        private void TimeTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            this.Time = this.GetIntTime(this.Text);
        }

        private int GetIntTime(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return -1;
            }

            int h = 0;
            int m = 0;
            int s = 0;
            string[] strs = value.Split(':');
            if (this.timeType == EnumTimeType.HourMinute)
            {
                if (strs[0].Length > 0)
                    h = int.Parse(strs[0]);
                if (strs.Length > 1 && strs[1].Length > 0)
                    m = int.Parse(strs[1]);
                this.Text = h.ToString("00") + ":" + m.ToString("00");
            }
            else if (this.timeType == EnumTimeType.MinuteSecond)
            {
                if (strs[0].Length > 0)
                    m = int.Parse(strs[0]);
                if (strs.Length > 1 && strs[1].Length > 0)
                    s = int.Parse(strs[1]);
                this.Text = m.ToString("00") + ":" + s.ToString("00");
            }
            else
            {
                if (strs[0].Length > 0)
                    h = int.Parse(strs[0]);
                if (strs.Length > 1 && strs[1].Length > 0)
                    m = int.Parse(strs[1]);
                if (strs.Length > 2 && strs[2].Length > 0)
                    s = int.Parse(strs[2]);
                this.Text = h.ToString("00") + ":" + m.ToString("00") + ":" + s.ToString("00");
            }

            return h * 3600 + m * 60 + s;
        }

        public int Time
        {
            get
            {
                return (int)GetValue(TimeProperty);
            }
            set
            {
                SetValue(TimeProperty, value);
            }
        }

        public TimeTextBox()
        {
            this.MaxLength = 8;
            this.LostFocus += new RoutedEventHandler(TimeTextBox_LostFocus);
            this.KeyDown += new KeyEventHandler(MaskedTextBox_KeyDown);
            this.KeyUp += new KeyEventHandler(MaskedTextBox_KeyUp);

            InputMethod.SetPreferredImeState(this, InputMethodState.On);
            InputMethod.SetPreferredImeConversionMode(this, ImeConversionModeValues.Alphanumeric);
            InputMethod.SetIsInputMethodEnabled(this, false);
        }

        void MaskedTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            //if (e.Key == Key.Back)
            //{
            //    if (this.Text.Length != 0)
            //    {
            //        if (this.SelectionStart != 0
            //            && this.Text[this.SelectionStart - 1] == ':')
            //        {
            //            this.SelectionStart--;
            //        }

            //        this.Text = this.Text.Substring(0, this.SelectionStart);
            //        this.SelectionStart = this.Text.Length;
            //    }
            //}
            //else
            if (this.inOnCtrl == true && (e.Key == Key.V || e.Key == Key.Ctrl))
            {
                if (this.checkTimeType(this.Text) == false)
                {
                    this.Text = this.textTemp;
                    this.SelectionStart = this.indexTemp;
                    this.SelectionLength = this.lengthTemp;
                }
                else
                {
                    reSetTimeText(this.Text);
                }
            }

            if (e.Key == Key.Shift)
            {
                inOnShift = false;
            }
            else if (e.Key == Key.Ctrl)
            {
                inOnCtrl = false;
            }
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.Key == Key.Back || e.Key == Key.Delete)
            {
                if (this.SelectionLength == this.Text.Length && this.IsNullable == true)
                {
                    this.Time = -1;
                    this.Text = string.Empty;
                }
                else
                {
                    char[] textList = this.Text.ToCharArray();
                    int length = 0;
                    int startIndex = 0;
                    if (e.Key == Key.Back)
                    {
                        if (string.IsNullOrEmpty(this.SelectedText))
                        {
                            length = 1;
                            startIndex = this.SelectionStart - 1;
                        }
                        else
                        {
                            length = this.SelectedText.Length;
                            startIndex = this.SelectionStart;

                        }
                    }
                    else if (e.Key == Key.Delete)
                    {
                        if (string.IsNullOrEmpty(this.SelectedText))
                        {
                            length = 1;
                            startIndex = this.SelectionStart;
                        }
                        else
                        {
                            length = this.SelectedText.Length;
                            startIndex = this.SelectionStart;
                        }
                    }

                    for (int i = startIndex; i < startIndex + length; i++)
                    {
                        if (i >= 0 && i < textList.Length)
                        {
                            if (textList[i] != ':')
                            {
                                textList[i] = '0';
                            }
                        }
                    }

                    this.Text = new string(textList);
                    if (startIndex >= 0 && startIndex < this.Text.Length)
                    {
                        this.SelectionLength = 0;
                        this.SelectionStart = e.Key == Key.Back ? startIndex : startIndex + 1;
                    }
                }
            }
            else
            {
                base.OnKeyDown(e);
            }
        }

        void MaskedTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Shift)
            {
                inOnShift = true;
            }
            else if (e.Key == Key.Ctrl)
            {
                if (this.checkTimeType(this.Text) == true)
                {
                    this.textTemp = this.Text;
                    this.indexTemp = this.SelectionStart;
                    this.lengthTemp = this.SelectionLength;
                }
                inOnCtrl = true;
            }

            if (this.inOnShift == true)
            {
                e.Handled = true;
                return;
            }

            int key = (int)e.Key;

            if (this.checkInput(this.SelectionStart, key) == true)
            {
                int index = this.SelectionStart;

                if (index == this.Text.Length)
                {
                    if (index == 2 || index == 5)
                    {
                        this.Text += ":";
                        this.SelectionStart = index + 1;
                    }
                }
                else
                {
                    if (this.Text[index] == ':')
                        index++;

                    this.Text = this.Text.Remove(index, 1);

                    this.SelectionStart = index;
                }
            }
            else if (e.Key != Key.Tab && e.Key != Key.Enter && e.Key != Key.Escape)
            {
                e.Handled = true;
                return;
            }
        }

        private bool checkTimeType(string value)
        {
            string reg;
            if (this.timeType == EnumTimeType.HourMinute)
            {
                reg = regHM;
            }
            else if (this.timeType == EnumTimeType.HourMinute)
            {
                reg = regMS;
            }
            else
            {
                reg = regHMS;
            }

            if (System.Text.RegularExpressions.Regex.IsMatch(value, reg))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private void reSetTimeText(string value)
        {
            int h = 0;
            int m = 0;
            int s = 0;
            string[] strs = value.Split(':');
            if (this.timeType == EnumTimeType.HourMinute)
            {
                if (strs[0].Length > 0)
                    h = int.Parse(strs[0]);
                if (strs.Length > 1 && strs[1].Length > 0)
                    m = int.Parse(strs[1]);
                this.Text = h.ToString("00") + ":" + m.ToString("00");
            }
            else if (this.timeType == EnumTimeType.MinuteSecond)
            {
                if (strs[0].Length > 0)
                    m = int.Parse(strs[0]);
                if (strs.Length > 1 && strs[1].Length > 0)
                    s = int.Parse(strs[1]);
                this.Text = m.ToString("00") + ":" + s.ToString("00");
            }
            else
            {
                if (strs[0].Length > 0)
                    h = int.Parse(strs[0]);
                if (strs.Length > 1 && strs[1].Length > 0)
                    m = int.Parse(strs[1]);
                if (strs.Length > 2 && strs[2].Length > 0)
                    s = int.Parse(strs[2]);
                this.Text = h.ToString("00") + ":" + m.ToString("00") + ":" + s.ToString("00");
            }
        }

        /// <summary>
        /// 入力値を判断する
        /// </summary>
        /// <param name="index">該当インデックス</param>
        /// <param name="value">入力値</param>
        /// <returns></returns>
        private bool checkInput(int index, int key)
        {
            if (this.timeType == EnumTimeType.HourMinute)
            {
                return this.checkHM(index, key);
            }
            else if (this.timeType == EnumTimeType.MinuteSecond)
            {
                return this.checkMS(index, key);
            }
            else
            {
                return this.checkHMS(index, key);
            }
        }

        private bool checkHMS(int index, int key)
        {
            switch (index)
            {
                //時
                case 0:
                    if (this.Text.Length > 1 && int.Parse(this.Text[1].ToString()) >= 4)
                        return this.checkNum(key, 1);
                    else
                        return this.checkNum(key, 2);
                case 1:
                    if (this.Text[0] == '2')
                        return this.checkNum(key, 3);
                    else
                        return this.checkNum(key, 9);
                //分
                case 2:
                case 3:
                    return this.checkNum(key, 5);
                case 4:
                    return this.checkNum(key, 9);
                //秒
                case 5:
                case 6:
                    return this.checkNum(key, 5);
                case 7:
                    return this.checkNum(key, 9);
                default:
                    return false;
            }
        }

        private bool checkHM(int index, int key)
        {
            switch (index)
            {
                //時
                case 0:
                    if (this.Text.Length > 1 && int.Parse(this.Text[1].ToString()) >= 4)
                        return this.checkNum(key, 1);
                    else
                        return this.checkNum(key, 2);
                case 1:
                    if (this.Text[0] == '2')
                        return this.checkNum(key, 3);
                    else
                        return this.checkNum(key, 9);
                //分
                case 2:
                case 3:
                    return this.checkNum(key, 5);
                case 4:
                    return this.checkNum(key, 9);
                default:
                    return false;
            }
        }

        private bool checkMS(int index, int key)
        {
            switch (index)
            {
                //分
                case 0:
                    return this.checkNum(key, 5);
                case 1:
                    return this.checkNum(key, 9);
                //秒
                case 2:
                case 3:
                    return this.checkNum(key, 5);
                case 4:
                    return this.checkNum(key, 9);
                default:
                    return false;
            }
        }

        private bool checkNum(int key, int num)
        {
            if (key >= 20 && key <= 20 + num)
            {
                return true;
            }
            else if (key >= 68 && key <= 68 + num)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
